package com.panfeng.xcloud.boss.provider.member.service.impl;

import com.alibaba.fastjson.JSON;
import com.panfeng.xcloud.boss.provider.member.constants.LockContants;
import com.panfeng.xcloud.boss.provider.member.dao.mapper.SegmentUidConfigMapperExt;
import com.panfeng.xcloud.boss.provider.member.dto.request.ChangeDefaultConfigReqDTO;
import com.panfeng.xcloud.boss.provider.member.service.ISeqenceService;
import com.panfeng.xcloud.boss.provider.member.service.IUserBasicService;
import com.panfeng.xcloud.common.core.configuration.DataDictionaryConfig;
import com.panfeng.xcloud.common.core.constants.RedisConstants;
import com.panfeng.xcloud.common.core.enums.DataDictionaryTypeEnum;
import com.panfeng.xcloud.common.core.utils.StringUtils;
import com.panfeng.xcloud.dao.member.entity.DataDictionary;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class SeqenceServiceImpl implements ISeqenceService {

    private final long DEFAULT_ACCOUNT_ID_B = 100000000001l;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private DataDictionaryConfig dataDictionaryConfig;

    @Autowired
    private IUserBasicService iUserBasicService;

    @Autowired
    private SegmentUidConfigMapperExt segmentUidConfigMapperExt;

    public static final String REGISTERRANK_LOCK = "REGISTERRANK_LOCK";

    private final static Lock lock = new ReentrantLock();

    /**
     * 数据库字典表中
     * 1、首字符初始值字典为a
     * 2、尾数初始化值为26001
     *
     * @param userType
     * @return
     */
    @Override
    public synchronized String getExtUserId(String userType) {
        lock.lock();
        String finalUid = "";
        try {
            AtomicInteger currentLastNum = new AtomicInteger();
            //首字符(不包含z ,z作为特殊靓号首字符)
            List<String> firtCharNotZ = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y");
            String currentFirstChar = this.getCurrentFirstChar();
            int currentNum = this.getCurrentLastNum();
            log.info(">>> 获取平台uid规则，当前首字母：{} ,当前尾数：{}<<<", currentFirstChar, currentNum);
            if (currentNum == (99999 - 1)) {
                int currentCharIndex = firtCharNotZ.indexOf(currentFirstChar);
                //切换到下一个字母
                log.info(">>> 开始切换下一个平台首字母:{} <<<", currentFirstChar);
                setCurrentFirstChar(firtCharNotZ.get(currentCharIndex + 1));
                //切换成新增的尾数00001
                setCurrentLastNum(00001);
            } else {
                currentLastNum.set(currentNum);
                setCurrentLastNum(currentLastNum.incrementAndGet());
            }
            finalUid = currentFirstChar + "" + String.format("%05d", currentNum);
            //撞库，匹配靓号库是否存在，如果存在则递归重新获取；
            if (isMatchSegmentUids(finalUid)) {
                return getExtUserId(userType);
            }
            log.info(">>> 平台生成的唯一uid:{} <<<", finalUid);
        } catch (Exception ex) {
            log.error(">>> 加速异常:{} <<<", ex);
        } finally {
            lock.unlock();
        }
        return finalUid;
    }

    /**
     * 程序启动时将靓号字段加载到redis缓存，匹配是否命中
     *
     * @param finalUid
     * @return
     */
    private boolean isMatchSegmentUids(String finalUid) {
        //先判断字典列表是否存在数据中，如果不存在重新查询数据来进行加载
        String segmentUidConfigJson = stringRedisTemplate.opsForValue().get(RedisConstants.XDCLOUD_BOSS_SEGMENT_UID_CONFIG_PREFIX);
        List<String> list = new ArrayList<>();
        if (StringUtils.isEmpty(segmentUidConfigJson)) {
            list = segmentUidConfigMapperExt.queryList();
            if (null != list && !list.isEmpty()) {
                stringRedisTemplate.opsForValue().set(RedisConstants.XDCLOUD_BOSS_SEGMENT_UID_CONFIG_PREFIX, JSON.toJSONString(list));
            }
        } else {
            list = JSON.parseArray(segmentUidConfigJson, String.class);
        }
        if (list.contains(finalUid)) {
            return true;
        }
        return false;
    }

    public static final String CURRENTLASTNUM_LOCK = "CURRENTLASTNUM_LOCK";

    /**
     * 设置当前尾号
     *
     * @param currentLastNum
     */
    private synchronized void setCurrentLastNum(int currentLastNum) {
        String lockKey = LockContants.XDCLOUD_LOCK_TASK_CURRENTLASTNUM_PREFIX + currentLastNum;
        log.info(">>>>> setCurrentLastNum 设置分布式锁，避免资源竞争，锁key {}  <<<<<", lockKey);
        boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, CURRENTLASTNUM_LOCK);
        stringRedisTemplate.expire(lockKey, 60, TimeUnit.SECONDS);
        try {
            if (lock) {
                stringRedisTemplate.opsForValue().set(RedisConstants.UID_CURRENTLASTNUM_PREFIX, String.valueOf(currentLastNum));
            } else {
                log.info("没有获取到锁，不更更新尾号：{},lockKey:{}", currentLastNum, lockKey);
            }
        } finally {
            if (lock) {
                stringRedisTemplate.delete(lockKey);
            } else {
                log.info("没有获取到锁,不需要释放锁，不更新尾号：{},lockKey:{}", currentLastNum, lockKey);
            }
        }
    }

    /**
     * 获取当前尾号
     *
     * @return
     */
    private synchronized int getCurrentLastNum() {
        String cacheCurrentLastNum = stringRedisTemplate.opsForValue().get(RedisConstants.UID_CURRENTLASTNUM_PREFIX);
        if (org.apache.commons.lang.StringUtils.isNotEmpty(cacheCurrentLastNum)) {
            return Integer.valueOf(cacheCurrentLastNum);
        }

        //查询字典表data_dictionary
        String currentCurrentLastNum = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.firstNum.getCode());
        stringRedisTemplate.opsForValue().set(RedisConstants.UID_CURRENTLASTNUM_PREFIX, currentCurrentLastNum);

        //查询字典表data_dictionary
        return Integer.valueOf(currentCurrentLastNum);
    }

    /**
     * 获取当前首字母
     *
     * @return
     */
    private synchronized String getCurrentFirstChar() {
        String cacheCurrentFirstChar = stringRedisTemplate.opsForValue().get(RedisConstants.XDCLOUD_BOSS_UID_CURRENT_FIRSTCHAR_PREFIX);
        if (StringUtils.isNotEmpty(cacheCurrentFirstChar)) {
            return cacheCurrentFirstChar;
        }
        //查询字典表data_dictionary
        String currentFirstchar = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.currentFirstchar.getCode());
        stringRedisTemplate.opsForValue().set(RedisConstants.XDCLOUD_BOSS_UID_CURRENT_FIRSTCHAR_PREFIX, currentFirstchar);
        return currentFirstchar;
    }

    public static final String FIRSTCHAR_LOCK = "FIRSTCHAR_LOCK";

    /**
     * 设置当前首字母
     *
     * @param firstChar
     */
    private void setCurrentFirstChar(String firstChar) {
        String lockKey = LockContants.XDCLOUD_LOCK_TASK_FIRSTCHAR_PREFIX + firstChar;
        log.info(">>>>> setCurrentFirstChar 设置分布式锁，避免资源竞争，锁key {}  <<<<<", lockKey);
        boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, FIRSTCHAR_LOCK);
        stringRedisTemplate.expire(lockKey, 60, TimeUnit.SECONDS);
        try {
            if (lock) {
                stringRedisTemplate.opsForValue().set(RedisConstants.XDCLOUD_BOSS_UID_CURRENT_FIRSTCHAR_PREFIX, firstChar);
                //更新字典表data_dictionary
                DataDictionary dataDictionary = new DataDictionary();
                dataDictionary.setCode(DataDictionaryTypeEnum.currentFirstchar.getCode());
                dataDictionary.setName(firstChar);
                //刷新内存的字典对象.更新数据库
                ChangeDefaultConfigReqDTO changeDefaultConfigReqDTO = new ChangeDefaultConfigReqDTO();
                changeDefaultConfigReqDTO.setCode(DataDictionaryTypeEnum.currentFirstchar.getCode());
                changeDefaultConfigReqDTO.setName(firstChar);
                iUserBasicService.changeDefaultConfig(changeDefaultConfigReqDTO);
            } else {
                log.info("没有获取到锁，不更新首字符：{},lockKey:{}", firstChar, lockKey);
            }
        } finally {
            if (lock) {
                stringRedisTemplate.delete(lockKey);
            } else {
                log.info("没有获取到锁,不需要释放锁，不更新首字符：{},lockKey:{}", firstChar, lockKey);
            }
        }
    }

    @Override
    public synchronized String getAccountId() {
        RedisAtomicLong counter = new RedisAtomicLong("generateAccountId", stringRedisTemplate.getConnectionFactory());
        long keyNum = counter.incrementAndGet();
        log.info(">>>>> generateAccountId keyNum = {}  <<<<<", keyNum);
        if (keyNum == 1) {
            counter.set(DEFAULT_ACCOUNT_ID_B);
            keyNum = DEFAULT_ACCOUNT_ID_B;
        }
        return String.valueOf(keyNum);
    }

    @Override
    public String getNickName() {
        StringBuffer sb = new StringBuffer();
        sb.append(StringUtils.getRandomNickName(7));
        String nickName = sb.toString();
        log.info(">>>>> getNickName nickName = {}  <<<<<", nickName);
        return nickName;
    }

    @Override
    public String getRegisterRank() {
        String registerRank = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.registerRank.getCode());
        log.info(">>>>> registerRank = {}  <<<<<", registerRank);
        return registerRank;
    }

    @Override
    public synchronized void setRegisterRank(Integer registerRank) {
        String lockKey = LockContants.XDCLOUD_LOCK_TASK_REGISTERRANK_PREFIX + registerRank;
        log.info(">>>>> setRegisterRank 设置分布式锁，避免资源竞争，锁key {}  <<<<<", lockKey);
        boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, REGISTERRANK_LOCK);
        stringRedisTemplate.expire(lockKey, 60, TimeUnit.SECONDS);
        try {
            if (lock) {
                log.info(">>>> 更新下一个注册等级字典,当前注册等级：{}", registerRank);
                //更新数据库,刷新内存
                ChangeDefaultConfigReqDTO changeDefaultConfigReqDTO = new ChangeDefaultConfigReqDTO();
                changeDefaultConfigReqDTO.setCode(DataDictionaryTypeEnum.registerRank.getCode());
                changeDefaultConfigReqDTO.setName(String.valueOf(registerRank + 1));
                iUserBasicService.changeDefaultConfig(changeDefaultConfigReqDTO);
            } else {
                log.info("没有获取到锁，不更注册等级：{},lockKey:{}", registerRank, lockKey);
            }
        } finally {
            if (lock) {
                stringRedisTemplate.delete(lockKey);
            } else {
                log.info("没有获取到锁,不需要释放锁，不更新注册等级：{},lockKey:{}", registerRank, lockKey);
            }
        }
    }

    @Override
    public String gainAccountId() {
        return StringUtils.getOrderIdByUUId(1);
    }

}
